package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import server.catalina.Engine;
import server.catalina.Service;
import server.catalina.mapper.Context;
import server.catalina.mapper.Host;
import server.catalina.mapper.Mapper;
import server.catalina.mapper.Wrapper;
import server.servlet.HttpServlet;
import server.thread.RequestProcessor;
import server.thread.ServerSocketProcessor;
import server.utils.ReadWebXml;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;

/**
 * Minicat的主类
 * 优化版本——使用多线程（使用线程池）
 */
public class Bootstrap6 {
    List<Service> serviceList = new ArrayList<>();


    /**
     * Minicat的启动入口
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap6 bootstrap = new Bootstrap6();
        try {
            //1. 调用init方法完成初始化
            bootstrap.init();
            //2. 启动监听
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void init()throws Exception{
        //加载Tomcat配置文件
        loadServerConfiguration();
    }



    /**
     * MiniCat启动需要初始化展开的一些操作
     */
    public void start() throws Exception {
        /**
         * 定义线程池
         */
        //基本大小
        int corePoolSize = 10;
        //最大
        int maxPoolSize = 50;
        //如果线程空闲的话，超过多久进行销毁
        long keepAliveTime = 100L;
        //上面keepAliveTime的单位
        TimeUnit unit = TimeUnit.SECONDS;
        //请求队列
        BlockingQueue<Runnable> workerQueue = new ArrayBlockingQueue<>(50);
        //线程工厂，使用默认的即可
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        //拒绝策略，如果任务太多处理不过来了，如何拒绝
        RejectedExecutionHandler handler = new AbortPolicy();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize
                ,maxPoolSize
                ,keepAliveTime
                ,unit
                ,workerQueue
                ,threadFactory
                ,handler);

        /**
         * 有多少个service监听多少个端口
         */
        for (Service service : serviceList) {
            ServerSocketProcessor serverSocketProcessor = new ServerSocketProcessor(service, threadPoolExecutor);
            serverSocketProcessor.start();
        }
    }


    private void loadServerConfiguration(){
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            //根元素
            Element rootElement = document.getRootElement();
            List<Element> listService = rootElement.selectNodes("//Service");

            for (Element element : listService) {

                Engine engine = new Engine();
                List<Element> hostList = element.selectNodes("//Host");
                String port = element.element("Connector").attributeValue("port");

                Mapper mapper = new Mapper();
                for (Element elementHost : hostList) {
                    String hostName = elementHost.attributeValue("name");
                    String appBase = elementHost.attributeValue("appBase");
                    Host host = getHost(appBase);
                    mapper.setHostMap(hostName,host);
                }

                engine.setMapper(mapper);

                Service service = new Service();
                service.setEngine(engine);
                service.setPort(Integer.parseInt(port));

                serviceList.add(service);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    private Host getHost(String appBase){
        Host host = new Host();
        File file = new File(appBase);
        if (file.exists() && file.isDirectory()) {
            File[] files = file.listFiles();
            for (File webApp : files) {
                String contextName = webApp.getName();
                String webAppPath = webApp.getPath();
                Context context = getContext(webAppPath);
                host.setContextMap(contextName,context);
            }
        }
        return host;
    }


    private Context getContext(String webAppPath){
        Context context = ReadWebXml.readWebXml(webAppPath);
        return context;
    }


}
